package com.duguo.dynamicmvc.factory.processor.controller;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import com.duguo.dynamicmvc.factory.ControllerBuilder;
import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.utils.BeanFactoryUtils;
import com.duguo.dynamicmvc.factory.utils.BeanUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.FieldUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.MethodUtils;


public class ControllerInjectionCreatorImpl implements  BeanFactoryAware,ControllerInjectionCreator{

	ControllerNameBuilder _controllerNameBuilder;
	BeanFactory _beanFactory;
	
    public void setupInjectionFeilds(CtClass controllerCtClass) throws Exception {
    	String controllerName=_controllerNameBuilder.getControllerPath(controllerCtClass.getName());
    	List<String> usedInjections=new ArrayList<String>();
    	Map<String,CtMethod> existInjections=getSetters(controllerCtClass);
    	setupInjectionFeilds(controllerCtClass,controllerName,existInjections,usedInjections);
    }

	public void injectionFeilds(Object controllerInstance) throws Exception {
    	String controllerName=_controllerNameBuilder.getControllerPath(controllerInstance.getClass().getName());
    	CtClass controllerCtClass=ClassPool.getDefault().get(controllerInstance.getClass().getName());
    	List<String> usedInjections=new ArrayList<String>();
    	Map<String,CtMethod> existInjections=getSetters(controllerCtClass);
    	injectionFeildsFromFactory(controllerInstance,controllerName,existInjections,usedInjections);
    	injectionFeildsByBean(controllerInstance,existInjections);
    }

	void injectionFeildsByBean(Object controllerInstance, Map<String, CtMethod> existInjections)  throws Exception{
		if(existInjections.size()>0){
			for(String feildName:existInjections.keySet()){
				Object bean=null;
				try{
					bean=_beanFactory.getBean(feildName);
				}catch(Exception ex){
					CtMethod method=existInjections.get(feildName);
					bean=BeanFactoryUtils.getBeanOfType(_beanFactory,ClassPool.getDefault().getClassLoader().loadClass(method.getParameterTypes()[0].getClass().getName()));
				}
				BeanUtils.setFieldValue(controllerInstance, feildName, bean);
				existInjections.remove(feildName);
			}
		}
    	if(existInjections.size()>0){
    		throw new RuntimeException("Cannot find the setters  for fields "+existInjections.keySet().toString());
    	}
	}

	void setupInjectionFeilds(CtClass controllerCtClass,String controllerPath,Map<String,CtMethod> existInjections,List<String> usedInjections)  throws Exception{
		Map<String,Object> injections;
		if(controllerPath.length()>0){
			injections=getInjections(controllerPath);
		}else{
			injections=getGloabbleInjections();
		}
		if(injections!=null && injections.size()>0){
			for(String injectionName:injections.keySet()){
				Object injectionBean=injections.get(injectionName);
				if(!existInjections.containsKey(injectionName) && !usedInjections.contains(injectionName)){
					usedInjections.add(injectionName);
					FieldUtils.setupField(controllerCtClass, ClassPool.getDefault().get(injectionBean.getClass().getName()), injectionName);
				}
			}
		}
		if(controllerPath.length()>1){
			setupInjectionFeilds(controllerCtClass,_controllerNameBuilder.getParentLevelName(controllerPath),existInjections, usedInjections);
		}else if(controllerPath.length()==1){
			setupInjectionFeilds(controllerCtClass,"",existInjections,usedInjections);
		}
	}

	void injectionFeildsFromFactory(Object controllerInstance,String controllerPath,Map<String,CtMethod> existInjections,List<String> usedInjections)  throws Exception{
		Map<String,Object> injections;
		if(controllerPath.length()>0){
			injections=getInjections(controllerPath);
		}else{
			injections=getGloabbleInjections();
		}
		if(injections!=null && injections.size()>0){
			for(String injectionName:injections.keySet()){
				Object injectionBean=injections.get(injectionName);
				if(!usedInjections.contains(injectionName)){
					if(injectionBean instanceof String){
						String setterName=BeanUtils.getSetterName(injectionName);
						CtClass controllerCtClass=ClassPool.getDefault().get(controllerInstance.getClass().getName());
						CtMethod setterCtMethod=MethodUtils.findMethod(controllerCtClass, setterName);
						if(setterCtMethod.getParameterTypes()[0]!=ClassPool.getDefault().get(String.class.getName())){
							injectionBean=_beanFactory.getBean((String)injectionBean);
						}
					}
					BeanUtils.setFieldValue(controllerInstance, injectionName, injectionBean);
					usedInjections.add(injectionName);
					existInjections.remove(injectionName);
				}
			}
		}
		if(controllerPath.length()>1){
			injectionFeildsFromFactory(controllerInstance,_controllerNameBuilder.getParentLevelName(controllerPath),existInjections, usedInjections);
		}else if(controllerPath.length()==1){
			injectionFeildsFromFactory(controllerInstance,"",existInjections,usedInjections);
		}
	}


    Map<String, CtMethod> getSetters(CtClass controllerCtClass)  throws Exception{
    	Map<String, CtMethod> setters=new LinkedHashMap<String, CtMethod>();
    	List<CtMethod> methods=MethodUtils.findMethodByPattern(controllerCtClass, "set[A-Z].*");
    	if(methods.size()>0){
    		for(CtMethod method:methods){
    			if(method.getParameterTypes().length==1 && method.getReturnType()==CtClass.voidType){
        			String feildName=BeanUtils.getFieldName(method.getName());
        			if(!feildName.equals("registedControllers") && !feildName.equals("dynamicController"))
        				setters.put(feildName, method);
    			}
    		}
    	}
		return setters;
	}

	private Map<String, Object> getInjections(String controllerPath) {
		try{
			ControllerFactory controllerFactory=(ControllerFactory)_beanFactory.getBean("&"+controllerPath);
			return controllerFactory.getInjections();
		}catch(Exception ex){
			return null;
		}
	}

	private Map<String, Object> getGloabbleInjections() {
		try{
			ControllerBuilder controllerBuilder=BeanFactoryUtils.getBeanOfType(_beanFactory,ControllerBuilder.class);
			return controllerBuilder.getGloableInjections();
		}catch(Exception ex){
			return null;
		}
	}

	public ControllerNameBuilder getControllerNameBuilder() {
		return _controllerNameBuilder;
	}

	public void setControllerNameBuilder(ControllerNameBuilder controllerNameBuilder) {
		_controllerNameBuilder = controllerNameBuilder;
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		_beanFactory = beanFactory;
	}
    
}
